package server

import (
	"context"
	"fmt"
	"net"
	"os"

	"gitee.com/crack007/goose/gsconfig"
	"gitee.com/crack007/goose/gscore"
	"gitee.com/crack007/goose/gslog"
	"gitee.com/crack007/goose/gsutil"
)

const (
	Server_Udp  = "udp"
	Server_Tcp  = "tcp"
	Server_Unix = "unix"
)

type Server struct {
	Host    string
	Port    uint16
	Network string
	handle  IServerHandle
	gscore.BaseServer
	sessionList    []*Session
	sessionBuilder *SessionBuilder
	ServerStatus   uint // 服务器状态 1-运行中,2-准备关闭，不接受任何请求
	SessionManager *sessionManager
	MessageManager *MessageManager
	isUnix         bool
	listener       net.Listener
	Builder        *ServerBuilder
	listenPacket   net.PacketConn
}

func (x *Server) SetPort(Port uint16) {
	x.Port = Port
}

func (x *Server) InjectHandle(iHandle gscore.IHandle) {
	if v, ok := iHandle.(IServerHandle); ok {
		x.handle = v
		return
	}
	panic("IServerHandle type error")
}

func (x *Server) OnBeforeInit() {
	x.handle.OnInjectServer(x)
	x.handle.OnBeforeInit()
}

func (x *Server) OnInitConfig() {
	var err error
	defer func() {
		if err != nil {
			panic("onInitConfig err: " + err.Error())
		}
	}()

	if x.Builder.GetConfigPath() != "" {
		gsconfig.ParseConfig(x.Builder.GetConfigPath(), x.Builder.ConfigObject)
		if v, ok := x.Builder.ConfigObject.(gscore.AppConfig); ok {
			if v.Log != nil {
				gslog.DefaultLog = gslog.NewConsoleLogger(v.Log)
			}
		}
	}

	if x.Builder.GetLogDebug() {
		gslog.DefaultLog.SetLogLevel(gslog.LEVEL_DEBUG)
	}

	if x.Builder.GetLogTrace() {
		gslog.DefaultLog.SetLogLevel(gslog.LEVEL_TRACE)
	}

	x.handle.OnInitConfig()
}

func (x *Server) OnInitInject() {

}

func (x *Server) OnInitService() {
	x.handle.OnInitService()
}

func (x *Server) OnInitRouter() {

}

func (x *Server) GetBuilder() gscore.IBuilder {
	return x.Builder
}

func (x *Server) OnBeforeRun() {
	x.MessageManager.init()
	x.handle.OnBeforeRun()
}

func (x *Server) OnBuild() {

}

func (x *Server) OnRun() {
	x.AddGracefulTask(gscore.NewGracefulTask(func(ctx context.Context) {
		if x.isUnix && gsutil.FileUtil.FileExist(x.Host) {
			err := os.Remove(x.Host)
			if err != nil {
				panic(err)
			}
		}

		defer func() {
			if x.isUnix {
				os.Remove(x.Host)
			}

			if e := recover(); e != nil {
				gslog.Error(e)
			}
		}()

		listenAddress := fmt.Sprintf(":%d", x.Port)
		if x.isUnix {
			listenAddress = x.Host
		}

		gslog.Warn("server start [%s] , network: %s, listen: [%s]", x.GetServerName(), x.Network, listenAddress)
		var err error
		if x.Network == Server_Udp {
			err = x.runUdpServer(listenAddress)
			if err != nil {
				panic(err)
			}
			return
		}

		x.listener, err = net.Listen(x.Network, listenAddress)
		if err != nil {
			panic(err)
		}

		err = x.handle.OnStart()
		if err != nil {
			panic(err)
		}

		for {
			conn, err := x.listener.Accept()
			if err != nil {
				gslog.Error(err)
				break
			}

			session := BuildSession(x, conn)
			go session.process()
		}
	}))
	go func() {
		<-x.Done()
		gslog.Warn("server quit [%s]", x.GetServerName())
		x.handle.OnShutdown()
		if x.listener != nil {
			x.listener.Close()
		}

		x.MessageManager.Close()
	}()

	x.GracefulExit()
}

func (x *Server) runUdpServer(listenAddress string) error {
	var err error
	x.listenPacket, err = net.ListenPacket(x.Network, listenAddress)
	if err != nil {
		return err
	}

	connMap := map[string]*Session{}
	x.handle.OnStart()
	for {
		var buf = make([]byte, 0xffff)
		n, addr, err := x.listenPacket.ReadFrom(buf)
		if err != nil {
			return err
		}

		var session *Session
		var ok bool
		if session, ok = connMap[addr.String()]; !ok {
			session = BuildUdpSession(x, x.listenPacket, addr)
			session.initUdp(func() {
				delete(connMap, addr.String())
			})
			connMap[addr.String()] = session
		}

		session.ProcessUdp(buf[:n])
	}
}
